Antipattern: The Model Is an Active Record

In simple applications, we don’t need a lot of custom logic in a model. It’s relatively straightforward to match the fields of a model object with the columns of a single table in a database. This is a type of object-relational mapping. All we need the object to do is know how to create, read, update and delete a row in the table — the basic CRUD operations.

Martin Fowler described a design pattern to support this mapping, called Active Record. Active Record is a data access pattern. We define a class corresponding to a table or view in our database. We can call a class method find() that returns an object instance of the class corresponding to an individual row in that table or view. We can also use the class constructor to create a new row. Calling save() on this object either inserts a new row or updates the existing row.

<?php
$bugsTable = Doctrine_Core::getTable('Bugs');
$bugsTable->find(1234);

$bug = new Bugs();
$bug->summary = "Crashes when I save";
$bug->save();
?>

Ruby on Rails popularized Active Record for web development frameworks in 2004, and now most web application frameworks use this pattern as their de facto data access object (DAO). There’s nothing wrong with using Active Record; it’s a fine pattern that provides a simple interface to individual rows in a single table. The antipattern is the convention that all model classes in an MVC application inherit from the base Active Record class. This is an example of the Golden Hammer antipattern: if the only tool one has is a hammer, they’ll treat everything as if it were a nail.

Leaky abstractions

It’s tempting to embrace any convention that simplifies software design. We can make our work easier if we’re willing to sacrifice some flexibility, and if we never really needed the flexibility to begin with, that’s even better.

But this is a fairy tale, like Jack and the Beanstalk. Jack believed that his magic beans would grow into a mighty beanstalk while he slept. Although it worked out all right in Jack’s story, we may not be so lucky everytime. Let’s look at the consequences of the Magic Beans antipattern.

Active Record couples models to the schema#

Active Record is a simple pattern because a plain Active Record class represents a single table or view in the database. The fields of each Active Record object match the columns in one corresponding table. If we have sixteen tables, we will define sixteen model subclasses.

This means that if we need to refactor our database to represent a new structure of data, our model classes need to change, as well as any code in our application that uses the model classes. Likewise, if we add a controller to handle a new screen in our application, we may have to duplicate the code that queries our models.

Active Record exposes CRUD functions#

The next problem we may run into is that other programmers who use our model class may bypass our intended usage and update data directly using CRUD functions.

For example, we might add a method assignUser() to a bug model because we need to send an email to that engineer after updating the bug.

<?php
class CustomBugs extends BaseBugs
{
  public function assignUser(Accounts $a)
  {
    $this->assigned_to = $a->account_id;
    $this->save();
    mail($a->email, "Assigned bug", "You are now responsible for bug #{$this->bug_id}.");
  }
}
?>

However, another programmer working on the bug application may bypass our method and assign the bug manually without sending the email.

<?php
$bugsTable = Doctrine_Core::getTable('Bugs');
$bug = $bugsTable->find(1234);
$bug->assigned_to = $user->account_id;
$bug->save();
?>

Our requirement was to have an email notification sent whenever the assignment changed, but the method above bypasses that step. Does it make sense for our derived model class to expose the CRUD methods of the base Active Record class? How can we prevent other programmers from using these methods inappropriately? How can we exclude the base Active Record interface from our model class’s generated documentation and code completion in programming editors?

Active Record encourages an anemic domain model#

A closely related point is that a model frequently has no behavior except generic CRUD methods. Many developers extend the base Active Record class without adding any new methods related to the work the model should do.

Treating models as simple data access objects encourages us to code our business logic outside the model, usually spread over multiple controller classes while reducing cohesion of the model’s behavior. Martin Fowler calls this antipattern the Anemic Domain Model in his blog. For example, we might have separate Active Record classes corresponding to the Bugs, Accounts, and Products tables. But we need data from all three of these tables in many application tasks.

Let’s look at a simple code example for our bug-tracking application that implements bug assignment, data entry, bug display, and bug search tasks. It uses a PHP framework called Doctrine to provide a simple active record interface, and it uses the Zend Framework for the MVC architecture.

<?php
class AdminController extends Zend_Controller_Action
{
  public function assignAction()
  {
    $bugsTable = Doctrine_Core::getTable("Bugs");
    $bug = $bugsTable->find($_POST["bug_id"]);
    $bug->Products[] = $_POST["product_id"];
    $bug->assigned_to = 
    $_POST["user_assigned_to"];
    $bug->save();
  }
}

class BugController extends Zend_Controller_Action
{
  public function enterAction()
  {
    $bug = new Bugs();
    $bug->summary = $_POST["summary"];
    $bug->description = 
    $_POST["description"];
    $bug->status = "NEW";

    $accountsTable = Doctrine_Core::getTable("Accounts");
    $auth = Zend_Auth::getInstance();
    if ($auth && $auth->hasIdentity()) {
      $bug->reported_by = $auth->getIdentity();
    }
    $bug->save();
  }

  public function displayAction()
  {
    $bugsTable = Doctrine_Core::getTable("Bugs");
    $this->view->bug = $bugsTable->find($_GET["bug_id"]);
    $accountsTable = Doctrine_Core::getTable("Accounts");
    $this->view->reportedBy = 
    $accountsTable->find($bug->reported_by);
    $this->view->assignedTo = $accountsTable->find($bug->assigned_to);
    $this->view->verifiedBy = $accountsTable->find($bug->verified_by);

    $productsTable = Doctrine_Core::getTable("Products");
    $this->view->products = $bug->Products;
  }
}

class SearchController extends Zend_Controller_Action 
{
  public function bugsAction() 
  { 
     $q = Doctrine_Query::create()
     ->from("Bugs b")
     ->join("b.Products p")
     ->where("b.status = ?", $_GET["status"])
     ->andWhere("MATCH(b.summary, b.description) AGAINST (?)", $_GET["search"]);
$this->view->searchResults = $q->fetchArray();
  }
}
?>

Code that uses Active Record in controller classes expands to become a procedural approach to organizing application logic. If the database schema or the application requirements ever change, we need to update many places in the code. If we add a controller, we need to write new code even if our queries against the model are similar to those in other controllers.

The class interaction diagram below is messy and hard to read; it only worsens as we add more controllers and DAO classes. This should be a strong clue that code that uses different models together is duplicated across controllers. We need to use another approach to simplify and encapsulate part of our application.

Using Magic Beans leads to vinelike tangles

Unit testing Magic Beans is difficult#

When we employ the Magic Beans antipattern, we find that testing each of the layers in MVC is harder.

  • Testing the model: Since we’ve made the model to be of the same class as the Active Record, we can’t test model behavior separately from data access. To test the model, we have to execute queries against a live database.

    Many people use “database fixtures”. A database fixture loads data into a test database to ensure that each test runs against a baseline state. Doing such complex setup and teardown operations makes testing models slow and error-prone, while also requiring a live database for running the tests.

  • Testing the view: Testing views involves rendering the view into HTML and parsing the result to verify that dynamic HTML elements provided by the models appear in the output. Even if our framework simplifies the assertions in our test scripts, the framework has to run complex and time-consuming code to perform the rendering and then parse the HTML for specific elements.

  • Testing the controller: We also find that testing the controller is complex because a model that is a data access object leads to repetitions of the same code in multiple controllers, all of which need to be tested.

    To test a controller, we need to create a fake HTTP request. The output of a web application is an HTTP response header and body. To verify the test, we have to pick apart the HTTP response that the controller returns. This needs a lot of setup code to test business logic, and it makes tests run slowly.

If we could separate business logic from database access and from presentation, it would help us meet the goals of MVC while making testing simpler as well.

Synopsis: Magic Beans
Solution: The Model Has an Active Record
Mark as Completed
Report an Issue